package com.zhny.aiot.three.valve.plugin;

import com.google.common.collect.ImmutableMap;
import com.zhny.aiot.plugin.ICenterFacilities;
import com.zhny.aiot.plugin.IDeviceFactory;
import com.zhny.aiot.plugin.core.utils.MessageUtils;
import com.zhny.aiot.plugin.model.DeviceEntity;
import com.zhny.aiot.plugin.model.MessageEntry;
import com.zhny.aiot.plugin.model.MessageType;
import com.zhny.aiot.plugin.model.TaskEntity;
import com.zhny.aiot.plugin.zhnygateway.GatewayEngine;
import com.zhny.aiot.plugin.zhnygateway.device.ZhnyConfig;
import com.zhny.aiot.plugin.zhnygateway.device.ZhnyDevice;
import com.zhny.aiot.plugin.zhnygateway.model.GatewayResponseMessage;
import com.zhny.aiot.plugin.zhnygateway.model.NodeRequestMessage;
import com.zhny.aiot.three.valve.plugin.model.ComParam;
import com.zhny.aiot.three.valve.plugin.model.ThreeValveStoreEntry;
import io.netty.buffer.ByteBuf;
import org.pf4j.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

public class ThreeValveDevice extends ZhnyDevice<ThreeValveStoreEntry> {
    private static final Logger logger = LoggerFactory.getLogger(ThreeValveDevice.class);
    private final Map<String, ComParam> CommandOperations;
    public ThreeValveDevice(GatewayEngine engine, IDeviceFactory<ZhnyConfig> factory, ICenterFacilities centerFacilities, DeviceEntity entity) {
        super(engine, factory, centerFacilities, entity);
        CommandOperations =  new ImmutableMap.Builder<String, ComParam>()
                .put("openValve1", new ComParam(0x31,getzhnyType()))
                .put("closeValve1", new ComParam(0x32,getzhnyType()))
                .put("openValve2", new ComParam(0x33,getzhnyType()))
                .put("closeValve2", new ComParam(0x34,getzhnyType()))
                .put("queryPressure", new ComParam( 0x3B,25))
                .put("queryFlow", new ComParam(0x4A,25))
                .put("queryTotalFlow", new ComParam(0x4C,25))
                .put("queryStatus", new ComParam(0x39,getzhnyType()))
                .put("queryVoltage", new ComParam(0x3A,getzhnyType())).build();
    }

    @Override
    public void onDecoder(GatewayResponseMessage message) {
        logger.info (message.toString ());
        int type = message.getDeviceType ();
        if(type == 0x10 || type == 0x19){
            logger.info("---start decoder three valve message---");
            onMessage(message);
        }
        if(message.getDeviceData() !=null)
            message.getDeviceData ().release ();
    }

    private void onMessage(GatewayResponseMessage message) {
        MessageEntry resultMsg = MessageUtils.createMessageEntry (this.getId ());
        resultMsg.setType(MessageType.RESULT);
        ThreeValveStoreEntry storeEntry = new ThreeValveStoreEntry ();
        int operation = message.getDeviceOper ();
        if(operation >= 0x31 && operation <= 0x34){
            if(message.getDataLength () == 7){
                resultMsg.setType (MessageType.ERROR);
                resultMsg.setData ("水利机械阀加入任务失败");
                logger.error ("[deviceNo:{},controllerNo:{}] 水利机械阀加入任务[{}]失败"
                        , getConfig ().getAddress (), getConfig ().getChannelNo (), operation);
                nofity (resultMsg);
                return;
            } else if(operation == 0x31){
                storeEntry.setValve1status (true);
                storeEntry.setVoltage (createVol(message.getDeviceData ().readUnsignedShort ()));
                resultMsg.setData (storeEntry);
            } else if(operation == 0x32){
                storeEntry.setValve1status (false);
                storeEntry.setVoltage (createVol(message.getDeviceData ().readUnsignedShort ()));
                resultMsg.setData (storeEntry);
            } else if(operation == 0x33){
                storeEntry.setValve2status (true);
                storeEntry.setVoltage (createVol(message.getDeviceData ().readUnsignedShort ()));
                resultMsg.setData (storeEntry);
            }else if(operation == 0x34){
                storeEntry.setValve2status (false);
                storeEntry.setVoltage (createVol(message.getDeviceData ().readUnsignedShort ()));
                resultMsg.setData (storeEntry);
            }
        } else if(operation == 0x39) {
            Boolean valve1Status = message.getDeviceData ().readUnsignedByte ()==1?true:false;
            storeEntry.setValve1status (valve1Status);
            Boolean valve2Status = message.getDeviceData ().readUnsignedByte ()==1?true:false;
            storeEntry.setValve2status (valve2Status);
            resultMsg.setData (storeEntry);
        }else if(operation == 0x3A){
            storeEntry = new ThreeValveStoreEntry();
            storeEntry.setVoltage (createVol(message.getDeviceData ().readUnsignedShort ()));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x3B){
            storeEntry = new ThreeValveStoreEntry();
            storeEntry.setPressure (createPressure(message.getDeviceData ().readUnsignedShort ()));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x4A){
            storeEntry = new ThreeValveStoreEntry();
            storeEntry.setFlow (createFlow(message.getDeviceData ()));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x4C){
            storeEntry = new ThreeValveStoreEntry();
            storeEntry.setTotalFlow (createTotalFlow(message.getDeviceData ()));
            resultMsg.setData (storeEntry);
        }


        logger.info("three valve store[{}]",storeEntry.toString());
        save (storeEntry);
        saveReal (storeEntry);
        nofity (resultMsg);
    }

    private Double createTotalFlow(ByteBuf buf) {
        int totalFlow1=((buf.readByte() << 24) &0xff000000|
                (buf.readByte() << 16) &0x00ff0000|
                (buf.readByte() << 8) &0x0000ff00 |
                (buf.readByte()) & 0x000000ff);
        int totalFlow=((buf.readByte() << 24) &0xff000000|
                (buf.readByte() << 16) &0x00ff0000|
                (buf.readByte() << 8) &0x0000ff00 |
                (buf.readByte()) & 0x000000ff);
        totalFlow = totalFlow1 + totalFlow;
        if (totalFlow <= 0)
            return 0.0;
        double value = totalFlow* 0.00067;
        return Double.parseDouble(String.format("%.2f",value * 1.0));
    }

    private Double createFlow(ByteBuf buf) {
        int flow=((buf.readByte() << 16) &0xff0000|
                (buf.readByte() << 8) &0x00ff00 |
                (buf.readByte()) & 0x0000ff);
        if (flow <= 0)
            return 0.0;
        double value = Math.sqrt (345 * flow + 1600)-40;
        return Double.parseDouble(String.format("%.2f",value * 1.0));
    }

    private double createPressure(int pressure) {
        double result =(((pressure/4096.0)*3.3)-0.5)/4.0;
        return  Double.parseDouble(String.format("%.2f",result));
    }

    private double createVol(int vol){
//        ((电压ADC/1024)*3.3*74)/12
        double result =((vol / 1024.00D)*3.30D*74.00D)/12.00D;
        return Double.parseDouble(String.format("%.2f",result));
    }


    @Override
    protected NodeRequestMessage onCommand(TaskEntity taskEntity) {
        if(StringUtils.isNullOrEmpty (taskEntity.getMethodName ()))
            return null;

        Map<String,Object> command = new HashMap<String, Object>();
        command.put (taskEntity.getMethodName (),null);
        logger.info ("config address[{}]",Integer.parseInt (getConfig ().getAddress ()));
        return encode (command);
    }

    private NodeRequestMessage encode(Map<String, Object> commandMap) {
        for (String id : commandMap.keySet ()){
            if(CommandOperations.containsKey(id)) {
                ComParam param=CommandOperations.get(id);
                Integer oper = param.getCommand();
                Integer deviceType = param.getDeviceType();
                int length=4;
                return new NodeRequestMessage(deviceType + 128, length, oper, Integer.parseInt(getConfig().getAddress()), null);
            }
        }
        return null;
    }

    @Override
    protected int getzhnyType() {
        return 16;
    }

    @Override
    protected void disposable() {

    }
}
